package zcj.stu.C47;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Configuration
public class C47_1 {
    public static void main(String[] args) throws Exception{
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(C47_1.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
//        testArray(beanFactory);
//        testList(beanFactory);
//        testContext(beanFactory);
          testName(beanFactory);
    }
    private static void testName(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service3"), false);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dd5.getDependencyType());
        for (String beanName : beanNames) {
            if(dd5.getDependencyName().equals(beanName)){
                System.out.println(dd5.resolveCandidate(beanName,dd5.getDependencyType(),beanFactory));
            }
        }
    }
    private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), false);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dd5.getDependencyType());
        for (String beanName : beanNames) {
            if (beanFactory.getMergedBeanDefinition(beanName).isPrimary()) {
                System.out.println(dd5.resolveCandidate(beanName,dd5.getDependencyType(),beanFactory));
                break;
            }
        }
    }
    private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);

        ContextAnnotationAutowireCandidateResolver resolver=new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dd5.getDependencyType());
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
            if(resolver.isAutowireCandidate(new BeanDefinitionHolder(beanDefinition,beanName),dd5)){
                System.out.println(dd5.resolveCandidate(beanName,dd5.getDependencyType(),beanFactory));
                break;
            }
        }
    }
    private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), false);

        ContextAnnotationAutowireCandidateResolver resolver=new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dd4.getDependencyType());
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            if(resolver.isAutowireCandidate(new BeanDefinitionHolder(beanDefinition,beanName),dd4)){
                System.out.println(dd4.resolveCandidate(beanName,dd4.getDependencyType(),beanFactory));
                break;
            }
        }
    }
    private static void testContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("context"), false);
        Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        resolvableDependencies.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
//         dependencies.forEach((k,v)->{
//             System.out.println("key:"+k+" value:"+v);
//         });
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {
                System.out.println(entry.getValue());
                break;
            }
        }
    }
    private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), false);
        if(dd2.getDependencyType()==List.class){
            Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();
            System.out.println(resolve);
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
            List<Object>list=new ArrayList<>();
            for (String beanName : beanNames) {
                list.add(dd2.resolveCandidate(beanName,resolve,beanFactory));
            }
            System.out.println(list);
        }
    }
    private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("services"), false);
        System.out.println(dd1.getDependencyType());
        if (dd1.getDependencyType().isArray()) {
            Class<?> componentType = dd1.getDependencyType().getComponentType();
            System.out.println(componentType);
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
            Object[]serviceArray=new Object[beanNames.length];
            for(int i=0;i<beanNames.length;i++){
                serviceArray[i]=dd1.resolveCandidate(beanNames[i],componentType,beanFactory);
            }
            System.out.println(serviceArray);
        }
    }
    static class Target{
        @Autowired
        private Service[]services;
        @Autowired
        private List<Service>serviceList;
        @Autowired
        private ConfigurableApplicationContext context;
        @Autowired
        private Dao<Integer>dao;
        @Autowired @Qualifier("service2")//1级
        private Service service;
        @Autowired
        private Service service3;//3级
    }
    static interface Dao<T>{}
    @Component
    static class Dao1 implements Dao<String>{}
    @Component
    static class Dao2 implements Dao<Integer>{}
    static interface Service{}
    @Component("service1")
    static class Service1 implements Service{}
    @Component("service2") @Primary//2级
    static class Service2 implements Service{}
    @Component("service3")
    static class Service3 implements Service{}

}
